package zp.demo.apitest.listener.apiconfig;

import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


@Component
@EnableConfigurationProperties(ApiProperties.class)
@RequiredArgsConstructor
public class ApiListener implements ApplicationListener<ApplicationStartedEvent> {

    final ApiProperties apiProperties;

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        ConfigurableApplicationContext applicationContext = event.getApplicationContext();
        //目前只需要获取RequestMappingHandlerMapping映射器
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods();

        Optional.ofNullable(handlerMethods)
                .filter(a->a.size()>0)
                .ifPresent(a->{
                    //请求类型list转set
                    Set<RequestMethod> all = Arrays.stream(RequestMethod.values()).collect(Collectors.toSet());
                    List<ApiInfo> list = a.entrySet().stream().map(b -> {
                        //获取方法列表
                        Set<RequestMethod> methods = Optional.ofNullable(b.getKey().getMethodsCondition().getMethods())
                                .filter(c -> c.size() > 0)
                                .orElseGet(() -> all);
                        //版本正则设置
                        Pattern versionMatch = apiProperties.getVersionMatch();
                        //地址匹配
                        AntPathMatcher antPathMatcher = new AntPathMatcher();

                        //名称 名字只能通过注解拿了，为了不增加工作量就直接使用swagger的注解
                        String name = Optional.ofNullable(b.getValue().getMethod().getAnnotation(ApiOperation.class))
                                .map(d -> d.value())
                                .orElse(b.getValue().getMethod().getName());
                        //循环地址
                        return Optional.ofNullable(b.getKey().getPathPatternsCondition().getPatterns())
                                .filter(c -> c.size() > 0)
                                .orElseGet(Collections::emptySet)
                                .stream()
                                //排除过滤
                                .filter(c->!apiProperties.exclude.stream().filter(d -> antPathMatcher.match(d, c.getPatternString()))
                                            .map(d -> true)
                                            .findFirst()
                                            .orElse(false)
                                )
                                //指定过滤
                                .filter(c->{
                                    if(apiProperties.only.size()==0)
                                    {
                                        return true;
                                    }
                                    return apiProperties.only.stream().filter(d -> antPathMatcher.match(d, c.getPatternString()))
                                            .map(d -> true)
                                            .findFirst()
                                            .orElse(false);
                                })
                                //转换为ApiInfo对象
                                .map(c -> {
                                    ApiInfo apiInfo = new ApiInfo();
                                    apiInfo.setRequestMethods(methods);
                                    apiInfo.setPath(c.getPatternString());
                                    //版本
                                    Matcher matcher = versionMatch.matcher(apiInfo.getPath());
                                    if(matcher.find())
                                    {
                                        apiInfo.setVersion(matcher.group(1));
                                    }

                                    apiInfo.setName(name);
                                    return apiInfo;
                                })
                                .collect(Collectors.toList());

                    })
                    .flatMap(b -> b.stream())
                    .collect(Collectors.toList());
                    apiProperties.setApiInfos(list);
                });


    }

    @Data
    public  class ApiInfo{
        /**
         * 请求名称
         */
        String name ;
        /**
         * 请求方法类型
         */
        Set<RequestMethod> requestMethods;
        /**
         * 请求路径
         */
        String path;
        /**
         * 版本
         */
        String version;

    }

}
